﻿using System;
using System.Collections.Generic;

using System.Text;
using GGSharp.Packets;
using GGSharp.Structures;

namespace GGSharp
{
    public class GG : GGConnection, IDisposable
    {
        public delegate void MessageReceivedDelegate(object sender, ReceivedMessagePacket msg);
        public delegate void PresenceReceivedDelegate(object sender, IStatusReply presence);
        public delegate void ErrorHandler(object sender, Exception ex);

        public event MessageReceivedDelegate MessageReceived;
        public event PresenceReceivedDelegate PresenceReceived;
        public event EventHandler LoggedIn;
        public event EventHandler LoggedOut;
        public event ErrorHandler Error;

        public event GGReceiver.PacketReceivedDelegate PacketReceived
        {
            add { _receiver.PacketReceived += value; }
            remove { _receiver.PacketReceived -= value; }
        }

        public event GGSender.PacketSentDelegate PacketSent
        {
            add { _sender.PacketSent += value; }
            remove { _sender.PacketSent -= value; }
        }

        private GGReceiver _receiver;
        private GGSender _sender;
        private GGUserList _userList;
        private GGPubDir _pubDir;
        private GGRadio _radio;

        protected ClientVersion _clientVersion = ClientVersion.v7_7_0_3315;
        protected uint _seed = 0;
        protected GGShowType _show = GGShowType.Available;
        protected string _showDescription = "";
        protected int _uin = 0;
        protected string _password = "";

        private bool _loggedIn = false;

        public GG()
        {
            Receiver = new GGReceiver(this);
            Sender = new GGSender(this);

            UserList = new GGUserList(Sender, Receiver);
            PubDir = new GGPubDir(Sender, Receiver);
            Radio = new GGRadio();

            Receiver.PacketReceived += new GGReceiver.PacketReceivedDelegate(Receiver_PacketReceived);
        }

        public bool IsLoggedIn
        {
            get { return _loggedIn; }
        }

        public int Number
        {
            get { return _uin; }
            set { _uin = value; }
        }

        public string Password
        {
            get { return _password; }
            set { _password = value; }
        }

        public GGPubDir PubDir
        {
            get { return _pubDir; }
            set { _pubDir = value; }
        }

        public GGRadio Radio
        {
            get { return _radio; }
            set { _radio = value; }
        }

        public GGReceiver Receiver
        {
            get { return _receiver; }
            set { _receiver = value; }
        }

        public uint Seed
        {
            get { return _seed; }
        }

        public GGSender Sender
        {
            get { return _sender; }
            set { _sender = value; }
        }

        public GGShowType Show
        {
            get { return _show; }
            set { SendPresence(value); }
        }

        public string ShowDescription
        {
            get { return _showDescription; }
            set { SendPresence(Show, value); }
        }

        public GGUserList UserList
        {
            get { return _userList; }
            set { _userList = value; }
        }

        public void Login()
        {
            Connect(this.Number);
        }

        protected void LoginInternal()
        {
            LoginPacket70 loginPacket = new LoginPacket70(Number, Password, Seed);
            loginPacket.ClientVersion = _clientVersion;
            loginPacket.Show = this.Show;
            loginPacket.Description = this.ShowDescription;
            Sender.Send(loginPacket);
        }

        public void Logout()
        {
            SendPresence(GGShowType.NotAvailableDescription, "");
        }

        public void SendPresence(GGShowType show)
        {
            _show = show;

            if (IsLoggedIn)
                Sender.Send(new NewStatusPacket(show, _showDescription));
        }

        public void SendPresence(GGShowType show, string description)
        {
            _showDescription = description;

            SendPresence(show);
        }

        void Receiver_PacketReceived(object sender, GGSharp.Packets.HeaderPacket packet)
        {
            switch (packet.Type)
            {
                case PacketType.GG_RECV_MSG:
                    OnMessageReceived(packet as ReceivedMessagePacket);
                    break;
                case PacketType.GG_STATUS77:
                case PacketType.GG_STATUS80:
                    OnPresenceReceived(packet as IStatusReply);
                    break;
                case PacketType.GG_WELCOME:
                    WelcomePacket welcomePacket = (WelcomePacket)packet;
                    _seed = welcomePacket.Seed;

                    LoginInternal();
                    break;
                case PacketType.GG_NOTIFY_REPLY77:
                case PacketType.GG_NOTIFY_REPLY80:
                    NotifyReply77Packet notify = (NotifyReply77Packet)packet;
                    if (notify.Replies != null)
                        foreach (NotifyReply77 notifyReply in notify.Replies)
                            OnPresenceReceived(notifyReply);

                    break;
                case PacketType.GG_DISCONNECTING:
                case PacketType.GG_DISCONNECTING2:
                    OnLoggedOut();
                    Disconnect();
                    break;
                case PacketType.GG_LOGIN_OK:
                    OnLoggedIn();
                    break;
                case PacketType.GG_NEED_EMAIL:
                    OnLoggedIn();
                    break;
                case PacketType.GG_LOGIN_FAILED:
                    OnError(new Exception("Login failed"));
                    OnLoggedOut();
                    break;
            }

        }

        protected virtual void OnError(Exception e)
        {
            if (Error != null)
                Error(this, e);
        }

        protected virtual void OnLoggedIn()
        {
            _loggedIn = true;

            if (LoggedIn != null)
                LoggedIn(this, EventArgs.Empty);
        }

        protected virtual void OnLoggedOut()
        {
            _loggedIn = false;

            if (LoggedOut != null)
                LoggedOut(this, EventArgs.Empty);

            _seed = 0;
        }

        protected virtual void OnPresenceReceived(IStatusReply presence)
        {
            if (PresenceReceived != null)
                PresenceReceived(this, presence);
        }

        protected virtual void OnMessageReceived(ReceivedMessagePacket msg)
        {
            if (MessageReceived != null)
                MessageReceived(this, msg);
        }

        public void Send(Packets.HeaderPacket packet)
        {
            if (IsLoggedIn)
                this.Sender.Send(packet);
        }


        #region IDisposable Members

        public void Dispose()
        {
            Receiver.Dispose();
            Sender.Dispose();
        }

        #endregion
    }
}
